package com.gzhryc.pay.adaptation.weixin;

import com.gzhryc.common.config.PropertyTools;
import com.gzhryc.common.json.JsonTools;
import com.gzhryc.common.logging.Logger;
import com.gzhryc.pay.adaptation.weixin.msg.advance_pay.*;
import com.gzhryc.pay.models.IPayOrder;
import com.gzhryc.pay.models.PreAuthCloseParam;
import com.gzhryc.pay.models.RentBillParam;
import com.gzhryc.pay.models.RevertBillParam;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Properties;

public class WXPayAdvancePayService implements Serializable{

	private static final long serialVersionUID = 1L;

	static Logger log = Logger.getLogger(WXPayAdvancePayService.class);

	String SCHEMA = "WECHATPAY2-SHA256-RSA2048";

	DefaultWXPayConfig config;
	P12File p12File;
	String privateKeyContent;

	public WXPayAdvancePayService(){}

	public WXPayAdvancePayService(Properties properties) {
		this.config = PropertyTools.build(properties).getObject(DefaultWXPayConfig.class);
	}

	public WXPayAdvancePayService(DefaultWXPayConfig config) {
		this.config = config;
		this.loadFiles();
	}

	private void loadFiles(){
		if (StringUtils.isNotBlank(config.getCertificatePath())) {
			String path = config.getCertificatePath();
			if(path.endsWith("/")){
				path = path + config.getMchId() + "/apiclient_cert.p12";
			}else{
				path = path +"/" + config.getMchId() + "/apiclient_cert.p12";
			}
			File file = new File(path);
			if (file.exists()) {
				p12File = new P12File();
				p12File.setFile(file);
				p12File.setPassword(config.getCertificatePwd());
			}

			String keyPath = config.getCertificatePath();
			if(keyPath.endsWith("/")){
				keyPath = keyPath + config.getMchId()+ "/apiclient_key.pem";
			}else{
				keyPath = keyPath +"/" + config.getMchId() + "/apiclient_key.pem";
			}
			File keyFile = new File(keyPath);
			if(keyFile.exists()){
				try {
					privateKeyContent = new String(Files.readAllBytes(Paths.get(keyPath)));
				} catch (IOException e) {
					log.error(e.getMessage(),e);
				}
			}else{
				log.error("未加载微信APPID：" + config.getAppId() + "的证书文件，路径：" + keyPath);
			}
		}else{
			log.error("未加载微信APPID：" + config.getAppId() + "的证书文件，未配置证书路径");
		}
	}

	String getToken(String method, String url, String body)
			throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, UnsupportedEncodingException {
		String nonceStr = WXPayUtils.buildNonceStr();
		Long timestamp = System.currentTimeMillis() / 1000;
		String message = null;
		if(StringUtils.isBlank(body)) {
			message = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n\n";
		}else{
			message = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
		}
		PrivateKey privateKey = WXPayUtils.readPrivateKey(privateKeyContent);
		Signature sign = Signature.getInstance("SHA256withRSA");
		sign.initSign(privateKey);
		sign.update(message.getBytes("utf-8"));
		String signature = Base64.getEncoder().encodeToString(sign.sign());

		return "mchid=\"" + this.config.getMchId() + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\""
				+ this.config.getCertificateSerialNo() + "\"," + "signature=\"" + signature + "\"";
	}

	public WXPayAdvancePayCreateResponse applyCreateOrder(IPayOrder preAuthOrder, RentBillParam param) {
		CloseableHttpResponse response = null;
		try {
			CloseableHttpClient httpClient = WXHttpClient.getHttpClient(config.getAppId(),p12File);
			URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com").setPath("/v3/payscore/serviceorder").build();

			WXPayAdvancePayCreateRequest request = new WXPayAdvancePayCreateRequest();
			request.setOut_order_no(preAuthOrder.getTradeNo());
			request.setAppid(config.getAppId());
			request.setService_id(this.config.getPreAuthServiceId());
			request.setService_introduction(preAuthOrder.getTitle());
			if (param.getStareDate() != null) {
				request.getTime_range().setStart_time(DateFormatUtils.format(param.getStareDate(), "yyyyMMddHHmmss"));
			} else {
				request.getTime_range().setStart_time("OnAccept");
			}
			request.getRisk_fund().setName("DEPOSIT");
			request.getRisk_fund().setAmount(preAuthOrder.getPayMoney());
			request.setLocation(new Location());
			request.getLocation().setStart_location(param.getRentAddress());
			request.setPost_payments(new ArrayList<PostPaymentItem>());

			PostPaymentItem paymentItem = new PostPaymentItem();
			paymentItem.setName("收费标准");
			paymentItem.setDescription(param.getDescribe());
			request.getPost_payments().add(paymentItem);

			request.setNotify_url(this.config.getPreAuthNotifyUrl());
			request.setOpenid(preAuthOrder.getPayerId());
			request.setNeed_user_confirm(true);

			String content = JsonTools.toJson(request);
			StringEntity strEntity = new StringEntity(content, ContentType.create("application/json", Consts.UTF_8));

			HttpPost httpPost = new HttpPost(uri);
			httpPost.addHeader("Accept", "application/json");
			httpPost.addHeader("Authorization", SCHEMA + " " + getToken("POST", uri.getPath(), content));
			httpPost.setEntity(strEntity);

			response = httpClient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String str = EntityUtils.toString(entity, Charset.forName("UTF-8"));
				log.info("微信创建免租借订单响应：" + str);
				WXPayAdvancePayCreateResponse apiResponse = JsonTools.fromJson(str, WXPayAdvancePayCreateResponse.class);
				return apiResponse;
			}
		} catch (Exception e) {
			log.error("微信申请免押支付错误，"+ e.getMessage(), e);
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public WXPayAdvancePayFinishResponse applyFinishOrder(IPayOrder preAuthOrder, RevertBillParam param) {
		CloseableHttpResponse response = null;
		try {
			CloseableHttpClient httpClient = WXHttpClient.getHttpClient(config.getAppId(),p12File);
			URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com")
					.setPath("/v3/payscore/serviceorder/" + preAuthOrder.getTradeNo() + "/complete").build();

			WXPayAdvancePayFinishRequest request = new WXPayAdvancePayFinishRequest();
			request.setAppid(config.getAppId());
			request.setService_id(this.config.getPreAuthServiceId());

			PostPaymentItem paymentItem = new PostPaymentItem();
			paymentItem.setName("收费标准");
			paymentItem.setDescription(param.getDescribe());
			request.getPost_payments().add(paymentItem);

			paymentItem = new PostPaymentItem();
			paymentItem.setName("使用时长");
			paymentItem.setDescription(param.getUseTimes());
			request.getPost_payments().add(paymentItem);

			request.setTotal_amount(param.getPayMoney());
			request.getTime_range().setEnd_time(DateFormatUtils.format(param.getRevertDate(), "yyyyMMddHHmmss"));
			request.getLocation().setEnd_location(param.getRevertAddress());
			request.setProfit_sharing(false);

			String content = JsonTools.toJson(request);
			StringEntity strEntity = new StringEntity(content, ContentType.create("application/json", Consts.UTF_8));

			HttpPost httpPost = new HttpPost(uri);
			httpPost.addHeader("Accept", "application/json");
			httpPost.addHeader("Authorization", SCHEMA + " " + getToken("POST", uri.getPath(), content));
			httpPost.setEntity(strEntity);

			response = httpClient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String str = EntityUtils.toString(entity, Charset.forName("UTF-8"));
				log.info("微信免押归还响应：" + str);
				WXPayAdvancePayFinishResponse apiResponse = JsonTools.fromJson(str, WXPayAdvancePayFinishResponse.class);
				return apiResponse;
			}
		} catch (Exception e) {
			log.error("归还出现错误，" + e.getMessage(), e);
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public WXPayAdvancePayQueryResponse queryByTradeNo(String tradeNo) {
		CloseableHttpResponse response = null;
		try {
			CloseableHttpClient httpClient = WXHttpClient.getHttpClient(config.getAppId(),p12File);
			URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com").setPath("/v3/payscore/serviceorder")
					.addParameter("out_order_no", tradeNo).addParameter("service_id", this.config.getPreAuthServiceId())
					.addParameter("appid", this.config.getAppId()).build();

			HttpGet httpGet = new HttpGet(uri);
			httpGet.addHeader("Accept", "application/json");
			httpGet.addHeader("Authorization", SCHEMA + " " + getToken("GET", uri.getPath() + "?" + uri.getQuery(), ""));

			response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String str = EntityUtils.toString(entity, Charset.forName("UTF-8"));
				log.info("微信免押查询响应：" + str);
				WXPayAdvancePayQueryResponse apiResponse = JsonTools.fromJson(str, WXPayAdvancePayQueryResponse.class);
				return apiResponse;
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public WXPayAdvancePayQueryResponse queryByExternalTradeNo(String externalTradeNo) {
		CloseableHttpResponse response = null;
		try {
			CloseableHttpClient httpClient = WXHttpClient.getHttpClient(config.getAppId(),p12File);
			URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com").setPath("/v3/payscore/serviceorder")
					.addParameter("query_id", externalTradeNo).addParameter("service_id", this.config.getPreAuthServiceId())
					.addParameter("appid", this.config.getAppId()).build();
			HttpGet httpGet = new HttpGet(uri);
			httpGet.addHeader("Accept", "application/json");
			httpGet.addHeader("Authorization", SCHEMA + " " + getToken("GET", uri.getPath() + "?" + uri.getQuery(), ""));

			response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String str = EntityUtils.toString(entity, Charset.forName("UTF-8"));
				log.info("微信免押查询响应：" + str);
				WXPayAdvancePayQueryResponse apiResponse = JsonTools.fromJson(str, WXPayAdvancePayQueryResponse.class);
				return apiResponse;
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public WXPayAdvancePayCloseResponse applyCloseOrder(PreAuthCloseParam param) {
		CloseableHttpResponse response = null;
		try {
			CloseableHttpClient httpClient = WXHttpClient.getHttpClient(config.getAppId(),p12File);
			URI uri = new URIBuilder().setScheme("https").setHost("api.mch.weixin.qq.com")
					.setPath("/v3/payscore/serviceorder/" + param.getTradeNo() + "/cancel").build();

			WXPayAdvancePayCloseRequest request = new WXPayAdvancePayCloseRequest();
			request.setAppid(config.getAppId());
			request.setService_id(this.config.getPreAuthServiceId());
			request.setReason(param.getReason());

			String content = JsonTools.toJson(request);
			StringEntity strEntity = new StringEntity(content, ContentType.create("application/json", Consts.UTF_8));

			HttpPost httpPost = new HttpPost(uri);
			httpPost.addHeader("Accept", "application/json");
			httpPost.addHeader("Authorization", SCHEMA + " " + getToken("POST", uri.getPath(), content));
			httpPost.setEntity(strEntity);

			response = httpClient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String str = EntityUtils.toString(entity, Charset.forName("UTF-8"));
				log.info("微信免押关闭响应：" + str);
				WXPayAdvancePayCloseResponse apiResponse = JsonTools.fromJson(str, WXPayAdvancePayCloseResponse.class);
				return apiResponse;
			}
		} catch (Exception e) {
			log.error("关闭订单出现错误，" + e.getMessage(), e);
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public DefaultWXPayConfig getConfig() {
		return this.config;
	}

	public void setP12File(P12File p12File) {
		this.p12File = p12File;
	}

	public void setPrivateKeyContent(String privateKeyContent) {
		this.privateKeyContent = privateKeyContent;
	}
}
